<template>
  <div class="go_bang_wrap flex-center">

    <div class="go_bang_box">
      <header class="flex-between">
        <div class="tip" v-html="bang_action.tip"></div>
        <div class="btn_group">
          <el-button type="primary">玩家对战</el-button>
          <el-button type="primary">人机对战</el-button>
        </div>
      </header>
      <canvas :key="bang_action.key" id="go_bang" width="500" height="500" @click="stepChese"></canvas>
      <footer class="flex-center"><el-button type="primary" @click="restart">重新开始</el-button></footer>
    </div>
  </div>
</template>

<script setup lang="ts">
let context: CanvasRenderingContext2D;//canvas 2d实例
const bang_action = reactive({
  key: 0,
  turnMe: true,//是否本轮
  winWays: [], // 记录所有的赢法[[0,0,0],[1,0,0],[2,0,0],[3,0,0],[4,0,1], [1,0,1],[2,0,1],[3,0,1],[4,0,1],[5,0,1]] //前两个是坐标，后一个是赢法id
  wwScore: [],//每种赢法下自己棋子的坐标情况[[2,2,1],[3,2,1]] //可查看每种赢法下棋子的个数
  opwScore: [],//每种赢法下对手棋子的坐标情况[[2,2,1],[3,2,1]]
  over: false,//是否结束
  allBlank: [],//所有的空白子[[1,2], [2,3]]
  tip: "",//棋盘提示
}) as any;
onMounted(() => {
  initChessBoard();
  initWinWays();
})
const initChessBoard = () => {
  const canvas = document.getElementById("go_bang") as HTMLCanvasElement;
  context = canvas.getContext("2d")!;
  context.strokeStyle = "#b9b9b9";
  for (let i = 0; i <= 15; i++) {
    context.moveTo(20, 20 + 30 * i);//起点
    context.lineTo(470, 20 + 30 * i)//终点
    context.stroke();
    context.moveTo(20 + 30 * i, 20);//起点
    context.lineTo(20 + 30 * i, 470)//终点
    context.stroke();
  }
}
const initWinWays = () => {
  let id = 0;
  // 横线赢法
  for (let i = 0; i < 15; i++) {
    for (let j = 0; j < 11; j++) {
      id++;
      for (let k = 0; k < 5; k++) {
        bang_action.winWays.push([i, j + k, id])
      }
    }
  }
  // 竖线赢法
  for (let i = 0; i < 11; i++) {
    for (let j = 0; j < 15; j++) {
      id++;
      for (let k = 0; k < 5; k++) {
        bang_action.winWays.push([i + k, j, id])
      }
    }
  }
  // 正斜线赢法
  for (let i = 4; i < 15; i++) {
    for (let j = 0; j < 11; j++) {
      id++;
      for (let k = 0; k < 5; k++) {
        bang_action.winWays.push([i - k, j + k, id])
      }
    }
  }
  // 反斜线赢法
  for (let i = 0; i < 11; i++) {
    for (let j = 0; j < 11; j++) {
      id++;
      for (let k = 0; k < 5; k++) {
        bang_action.winWays.push([i + k, j + k, id])
      }
    }
  }
  for (let i = 0; i < 15; i++) {
    for (let j = 0; j < 15; j++) {
      bang_action.allBlank.push([i, j])
    }
  }
  // console.log(bang_action.winWays);

}
const stepChese = (e: MouseEvent, coordenate?: AnyObject, isComputer = true) => {
  if (bang_action.over) return;

  // bang_action.tip = `<p>ai思考中<span class="wait-loader"><span></p>`;
  console.log("你好");

  const x = coordenate?._x || Math.floor(e.offsetX / 30);
  const y = coordenate?._y || Math.floor(e.offsetY / 30);
  const findIndex = bang_action.allBlank.findIndex((_item: any[]) => JSON.stringify(_item) == JSON.stringify([x, y]));
  if (findIndex>=0) {
    bang_action.allBlank.splice(findIndex, 1);
  }else{
    return
  }


  context.beginPath();
  // 圆心坐标、半径、从0度到2pi度画个完整的圆出来
  context.fillStyle = bang_action.turnMe ? "black" : "red";
  context.arc(20 + 30 * x, 20 + 30 * y, 13, 0, 2 * Math.PI);
  context.closePath();
  context.fill();

  isOver(x, y);
  // bang_action.turnMe = false;
  bang_action.turnMe = !bang_action.turnMe;
  if (isComputer) {
    setTimeout(() => {
      computerChese();
      bang_action.turnMe = true;
      // bang_action.tip = "";
    }, 0);
  }
}
const computerChese = () => {
  console.log("kaka");
  let bool = true;
  // 查看玩家每颗子的得分，拿到最高得分和坐标(这个分数机制如何算)
  // 预算下一步棋：把空白子模拟都点击一遍，只拦截危险棋路（大于两条【三线相连或四线中间空一子】，
  for (let h = 0; h < bang_action.allBlank.length; h++) {
    console.log("我是bool万", bool);

    if (!bool) return;

    // 获取最高得分即对应的空白子
    // const hasWin = bang_action.winWays.filter((_item2:AnyObject)=>)
    // 拦截危险棋路
    // 获取当前点击空白子所有的赢法编号
    const [_x, _y] = bang_action.allBlank[h];
    // 这里模拟的范围太大了，可做优化
    // 获取这颗棋子对应编号下的棋子状况

    const { ImitateLayout } = isOver(_x, _y, true) as AnyObject;
    // 四颗子相连，两边没有被堵，拦截(插入的这颗子是其中的一头一尾，并且下一颗棋子不是空白子表示四子相连)
    if (ImitateLayout.find((_item: any) => {
      if (_item.coordenate.length == 4) {
        const x_axis = [_item.coordenate[0][0], _item.coordenate[1][0], _item.coordenate[2][0], _item.coordenate[3][0]].sort();
        const y_axis = [_item.coordenate[0][1], _item.coordenate[1][1], _item.coordenate[2][1], _item.coordenate[3][1]].sort();
        // 竖线
        if (x_axis[0] === x_axis[1] && ((y_axis[0] === _y && ((_y + 1) === y_axis[1])) || (y_axis[3] === _y && ((_y - 1) === y_axis[2])))) {
          return true
        } else {
          if ((x_axis[0] === _x && ((_x + 1) === x_axis[1])) || (x_axis[3] === _x && ((_x - 1) === x_axis[2]))) {
            return true
          }
        }
      }
    })) {

      stepChese(window.event as MouseEvent, { _x, _y }, false)

      bool = false;
      alert("下一步" + bool)
      return;
    }

    // 大于三颗子，[三线相连或四线中间空一子][[0,0,0],[1,1,0]] （实际上就是这3颗子，不要出现两个相连的白子就行）（当前只检测一条）
    const trArr = ImitateLayout.filter((_item: any) => {
      if (_item.coordenate.length >= 3) {
        // 横线
        if ((_item.coordenate[0][1] == _item.coordenate[1][1] == _item.coordenate[2][1])) {
          const x_axis = [_item.coordenate[0][0], _item.coordenate[1][0], _item.coordenate[2][0]].sort();
          if ((x_axis[2] - 3) == x_axis[1] || (x_axis[1] - 3) == x_axis[0]) {
            return false;
          } else {
            _item.type = "horizon"
            return true;
          }
          // 竖线
        } else if (_item.coordenate[0][0] == _item.coordenate[1][0] == _item.coordenate[2][0]) {
          const y_axis = [_item.coordenate[0][1], _item.coordenate[1][1], _item.coordenate[2][1]].sort();
          if ((y_axis[2] - 3) == y_axis[1] || (y_axis[1] - 3) == y_axis[0]) {
            return false;
          } else {
            _item.type = "vertical"
            return true;
          }
          // 斜线
          // 如果当前这种赢法既不是横线赢法，也不是竖线赢法，就一定是斜线赢法
        } else {
          const b_axis = [{ x: _item.coordenate[0][0], y: _item.coordenate[0][1] }, { x: _item.coordenate[1][0], y: _item.coordenate[1][1] }, { x: _item.coordenate[2][0], y: _item.coordenate[2][1] }].sort((n1, n2) => n1.y - n2.y);
          if ((b_axis[2].x - 3) == b_axis[1].x || (b_axis[1].x - 3) == b_axis[0].x) {
            return false;
          } else {
            if (b_axis[0].x > b_axis[1].x) {
              _item.type = "rslant"
            } else {
              _item.type = "lslant"
            }
            return true;
          }
        }
      } else {
        return false;
      }
    }); //[{id:1.coo:[[1,0,0],[2,0,0],[3,0,0]]}]

    trArr && trArr.sort((h1: { id: number; }, h2: { id: number; }) => h1.id - h2.id)
    console.log("哈哈哈哈", trArr);
    // 下的这颗棋子同时在trArr中至少存在两个(表示该子落下同时存在两种赢法)
    // 特殊情况，如果这颗子所在的两种赢法是同种类型，pass
    // let coexist = 0;
    let coexist = [{ type: "horizon", num: 0 }, { type: "vetical", num: 0 }, { type: "lslant", num: 0 }, { type: "rslant", num: 0 }];
    for (let i = 1; i < trArr.length; i++) {
      const findIndex = trArr[i].coordenate.find((_item: any) => { const [__x, __y] = _item; return __x === _x && __y == _y; })
      if (findIndex >= 0) {
        const realIndex = findIndex == 0 ? findIndex : (findIndex - 1);
        // 找到的是第一个
        // 横线
        if (trArr[i].coordenate[realIndex][1] == trArr[i].coordenate[realIndex + 1][1]) {
          if (goStep(coexist, coexist[0], _x, _y)) return bool = false;
          // 竖线
        } else if (trArr[i].coordenate[realIndex][0] == trArr[i].coordenate[realIndex + 1][0]) {
          if (goStep(coexist, coexist[1], _x, _y)) return bool = false;
          // 左斜线
        } else if (trArr[i].coordenate[realIndex][0] > trArr[i].coordenate[realIndex + 1][0]) {
          if (goStep(coexist, coexist[2], _x, _y)) return bool = false;
        } else {
          if (goStep(coexist, coexist[3], _x, _y)) return bool = false;
        }
      }
    }

  }
  // bang_action.allBlank.forEach((_item: number[]) => {
  // });

}
const goStep = (coexist: any[], coexistItem: any, ...reset: any[]) => {
  const [_x, _y] = reset;
  const findItem = coexist.find((_item: any) => _item.num > 0);
  if (findItem && findItem.type != coexistItem.type) {
    stepChese(window.event as MouseEvent, { _x, _y }, false)
    return true;
  } else {
    coexistItem.num++;
    return false;
  }
}
const isOver = (x: number, y: number, needImitate = false) => {
  // console.log(x, y);
  // 如果需要模拟棋局，需要将模拟后的棋盘状况返回

  const curScore = bang_action.turnMe ? bang_action.wwScore : bang_action.opwScore;
  let ImitateLayout: any[] = [];
  let num: number[] = [];

  bang_action.winWays.forEach((_item: any[]) => {
    const [_x, _y, _z] = _item;
    // 这颗棋存在赢法中
    if (x == _x && y == _y) {

      if (needImitate) {
        const filterScore = bang_action.wwScore.filter((_item2: any) => {
          const [__x, __y, __z] = _item2;
          return _z === __z
        })
        // 获取当前赢法编号下的坐标情况
        filterScore && ImitateLayout.push({ id: _z, coordenate: [_item, ...filterScore] })

        return;
      }
      // 按照curScore的赢法坐标二分法插入
      if (curScore.length > 0) {
        const findIndex = searchRange(curScore, _item);

        curScore.splice(findIndex, 0, _item);

      } else {

        curScore.push(_item)

      }
    }
  })
  if (needImitate) return { ImitateLayout, num };
  // 一种赢法有五颗棋子及表示胜利

  // console.log(curScore);
  if (curScore.length >= 5) {
    for (let i = 4; i < curScore.length; i++) {
      if (curScore[i][2] == curScore[i - 4][2]) {
        bang_action.over = true;
        bang_action.tip = bang_action.turnMe ? "你赢了" : "计算机赢了";
        return
      }
    }
  }

  // bang_action.winWays.forEach((_item: AnyObject) => {
  //   _item.coordinate.forEach((_coord: any[]) => {
  //     const [_x, _y] = _coord;
  //     if (x == _x && y == _y) {
  //       const curScore = bang_action.turnMe ? bang_action.wwScore : bang_action.opwScore;
  //       const findItem = curScore.find((_ws: AnyObject) => _ws.id == _item.id);

  //       if (findItem) {
  //         findItem.coordinate.push(_coord)
  //       } else {
  //         curScore.push({ id: _item.id, coordinate: [_coord] })
  //       }
  //       try {
  //         if (findItem && findItem.coordinate.length == 5) {
  //           bang_action.over = true;
  //           bang_action.tip = bang_action.turnMe ? "你赢了" : "计算机赢了";
  //           throw new Error(bang_action.tip);
  //         }
  //       } catch (error: any) {
  //         console.log(error.message);
  //       }
  //     }
  //   })
  // });
}
// target为赢法坐标
// 二分法插入，优先按照赢法坐标排序，其次按照x排序，再其次为y
const searchRange = (nums: any[], target: number[]) => {
  const [_x, _y, _z] = target;
  let start = 0, end = nums.length - 1
  if (_z <= nums[start][2]) {
    return 0
  }

  if (_z >= nums[end][2]) {
    return end + 1;
  }

  while (start <= end) {
    let mid = Math.floor((start + end) / 2) //10, 4, 7,5
    if (nums[mid][2] > _z) {
      end = mid - 1     //20,9,6,4
    } else {
      start = mid + 1    //0,5
    }
  }
  // 当赢法坐标相等时有才又要检测x,y
  if (_z == nums[start][2]) {
    // 由于优先向前插入，这块检测向右侧推进排序
    while (true) {
      if (_x <= nums[start][0]) {
        if (_y <= nums[start][1]) {
          break;
        }
      }
      ++start;
    }
  }
  return start;
};
const restart = () => {
  ++bang_action.key;
  Object.assign(bang_action, {
    turnMe: true,
    wwScore: [],
    opwScore: [],
    over: false,
    tip: ""
  })
  nextTick(() => initChessBoard())
}
</script>

<style scoped lang="scss">
@import "./index.scss";
</style>
